/*
 * ui.cpp
 *
 *  Created on: Oct 12, 2011
 *      Author: welcome
 */
#include <KD/kd.h>
#include <ui.h>
#include <gg2d/gg2d.h>
#include <rdpdr.h>
#include <sys/select.h>
#include <errno.h>
#include <string.h>
#include <GLES/gl.h>
#include <cache.h>
#include <cliprdr.h>
#include <CGameBase.h>

static gg2D *s_gg2d;
#define TEXTURE_PAGE_SIZE	256
extern int g_server_depth;
static int g_x_socket;
extern RD_BOOL g_owncolmap;
static uint32 *g_colmap = NULL;
typedef struct {
	uint32 red;
	uint32 green;
	uint32 blue;
} PixelColour;

#define SCREEN_FLAG		( \
				GG2D_OFFSCREEN_IF_FAST | \
/*				GG2D_OFFSCREEN_BILINEAR |*/ \
				GG2D_INTEGRAL_MAGNIFY |  \
				GG2D_STRETCH |\
				GG2D_HALIGN_CENTER | \
				GG2D_VALIGN_CENTER)

#define SPLITCOLOUR15(colour, rv) \
{ \
	rv.red = ((colour >> 7) & 0xf8) | ((colour >> 12) & 0x7); \
	rv.green = ((colour >> 2) & 0xf8) | ((colour >> 8) & 0x7); \
	rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
}

#define SPLITCOLOUR16(colour, rv) \
{ \
	rv.red =   (((colour >> 11) & 0x1f) * 255 + 15) / 31;\
	rv.green = (((colour >> 5) & 0x3f) * 255 + 31) / 63;\
	rv.blue =  ((colour & 0x1f) * 255 + 15) / 31;\
} \

#define SPLITCOLOUR24(colour, rv) \
{ \
	rv.blue = (colour & 0xff0000) >> 16; \
	rv.green = (colour & 0x00ff00) >> 8; \
	rv.red = (colour & 0x0000ff); \
}
#define DO_GLYPH(mixmode, ttext,idx) \
{\
  glyph = cache_get_font (font, ttext[idx]);\
  if (!(flags & TEXT2_IMPLICIT_X))\
  {\
    xyoffset = ttext[++idx];\
    if ((xyoffset & 0x80))\
    {\
      if (flags & TEXT2_VERTICAL)\
        y += ttext[idx+1] | (ttext[idx+2] << 8);\
      else\
        x += ttext[idx+1] | (ttext[idx+2] << 8);\
      idx += 2;\
    }\
    else\
    {\
      if (flags & TEXT2_VERTICAL)\
        y += xyoffset;\
      else\
        x += xyoffset;\
    }\
  }\
  if (glyph != NULL)\
  {\
    x1 = x + glyph->offset;\
    y1 = y + glyph->baseline;\
	ui_draw_glyph(mixmode, x1, y1, glyph->width, glyph->height, (RD_HGLYPH) glyph->pixmap, 0, 0, fgcolour, bgcolour); \
	if (flags & TEXT2_IMPLICIT_X)\
      x += glyph->width;\
  }\
}
void convertColor(unsigned int colour, PixelColour* pColor) {
	switch (g_server_depth) {
	case 15:
		SPLITCOLOUR15(colour, (*pColor))
		;
		break;
	case 16:
		SPLITCOLOUR16(colour, (*pColor))
		;
		break;
	case 24:
	case 32:
		SPLITCOLOUR24(colour, (*pColor))
		;
		break;
	default:
		pColor->red = 0;
		pColor->green = 0;
		pColor->blue = 0;
		break;
	}
}
/*--------------------------------*/
RD_BOOL ui_init(KDint width, KDint height) {
	if (!s_gg2d)
		s_gg2d = gg2D_create(GG2D_GLES1, 0, TEXTURE_PAGE_SIZE);
	gg2D_setScreen(s_gg2d, width, height, SCREEN_FLAG);
	return True;
}
void ui_deinit(void) {
	if (!s_gg2d)
		gg2D_release(s_gg2d);
}
RD_BOOL ui_create_window(void) {
	return True;
}
void ui_resize_window(void) {

}
void ui_destroy_window(void) {

}
void xwin_toggle_fullscreen(void) {

}
KDint xwin_process_events() {
	GGEngine::CGameBase* gameBase = GGEngine::CGameBase::GetInstance();
	for (;;) {
		const KDEvent *e = kdWaitEvent((gameBase->isPaused
				|| !gameBase->isFocused) ? -1 : 0);
		if (e == KD_NULL)
			break;
		if (e) {
			gameBase->EventCallback(e);
			if (gameBase->isQuitting)
				break;
		}
	}
	if (gameBase->isQuitting)
		return 0;
	else
		return 1;
}
int ui_select(int rdp_socket) {
	int n;
	fd_set rfds, wfds;
	struct timeval tv;
	RD_BOOL s_timeout = False;
	while (True) {
		n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket;
		if (!xwin_process_events()) {
			return 0;
		}
		FD_ZERO(&rfds);
		FD_ZERO(&wfds);
		FD_SET(rdp_socket, &rfds);
		FD_SET(g_x_socket, &rfds);

		/* default timeout */
		tv.tv_sec = 60;
		tv.tv_usec = 0;

		/* add redirection handles */
		rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);

		n++;

		switch (select(n, &rfds, &wfds, NULL, &tv)) {
		case -1:
			error("select: %s\n", strerror(errno));

		case 0:
			/* Abort serial read calls */
			if (s_timeout)
				rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) True);
			continue;
		}
		rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False);

		if (FD_ISSET(rdp_socket, &rfds))
			return 1;

	}
}
void ui_move_pointer(int x, int y) {

}
RD_HBITMAP ui_create_bitmap(int width, int height, uint8 * data) {
	gg2DRawImage rawImage;
	gg2DImage* image = KD_NULL;

	gg2DRawImage_initialize(&rawImage);

	rawImage.m_width = width;
	rawImage.m_height = height;
	rawImage.m_buffer = data;
	rawImage.m_pitch = width * (g_server_depth / 8);

	switch (g_server_depth) {
	case 24:
		rawImage.m_format = GG2D_R8G8B8;
		break;
	case 16:
		rawImage.m_format = GG2D_R5G6B5;
		break;
	case 15:
		rawImage.m_format = GG2D_INVALID_PIXEL_FORMAT;
		break;
	case 8:
		rawImage.m_format = GG2D_A8;
		break;
	}
	if (gg2DRawImage_convert(&rawImage, gg2D_getAllocator(s_gg2d),
			GG2D_R8G8B8A8, 0) != GG2D_NO_ERROR) {
		return KD_NULL;
	}
	image = gg2DImage_create(s_gg2d, rawImage.m_width, rawImage.m_height,
			GG2D_R8G8B8A8, GG2D_PERFORMANCE, rawImage.m_pitch,
			rawImage.m_buffer);
	gg2DRawImage_release(&rawImage, gg2D_getAllocator(s_gg2d));
	if (image == KD_NULL) {
		printf("image == KD_NULL \n");
	}
	return image;
}
void ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height,
		uint8 * data) {
	printf("ui_paint_bitmap \n");
	RD_HBITMAP image = ui_create_bitmap(width, height, data);
	gg2D_beginRender(s_gg2d, 0);
	gg2D_blitScaled(s_gg2d, (gg2DImage*) image, 0xffffffff, GG2D_CLIP_ORIGINAL,
			x, y, cx, cy, 0, 0, width, height);
	gg2D_endRender(s_gg2d);
}
void ui_destroy_bitmap(RD_HBITMAP bmp) {
	gg2DImage_release((gg2DImage*) bmp);
}
RD_HGLYPH ui_create_glyph(int width, int height, uint8 * src) {

	int scanline = ((int) width + 7) / 8;

	uint8* data = (uint8*) kdMalloc(width * height);
	uint8* row = KD_NULL;
	kdMemset(data, 0x00, sizeof(data));

	for (int i = 0; i < height; i++) {
		row = data + i * width;
		for (int j = 0; j < width; j++) {
			uint8 value = (src[i * scanline] & (0x80 >> j)) >> (8 - (j + 1));
			data[i * width + j] = (value == 1) ? 0xff : 0;
		}
		printf("\n");
	}
	gg2DRawImage rawImage;
	gg2DImage* bitmap = KD_NULL;
	gg2DRawImage_initialize(&rawImage);

	rawImage.m_width = width;
	rawImage.m_height = height;
	rawImage.m_buffer = data;
	rawImage.m_pitch = width;
	rawImage.m_format = GG2D_A8;

	if (gg2DRawImage_convert(&rawImage, gg2D_getAllocator(s_gg2d),
			GG2D_R8G8B8A8, 0) != GG2D_NO_ERROR) {
		return KD_NULL;
	}
	bitmap = gg2DImage_create(s_gg2d, rawImage.m_width, rawImage.m_height,
			GG2D_R8G8B8A8, GG2D_PERFORMANCE, rawImage.m_pitch,
			rawImage.m_buffer);
	gg2DRawImage_release(&rawImage, gg2D_getAllocator(s_gg2d));
	return (RD_HGLYPH) bitmap;
}
void ui_destroy_glyph(RD_HGLYPH glyph) {
	if (glyph)
		gg2DImage_release((gg2DImage*) glyph);
}
void ui_draw_glyph(int mixmode, int x, int y, int cx, int cy, RD_HGLYPH glyph,
		int srcx, int srcy, int bgcolour, int fgcolour) {
	if (s_gg2d) {
		PixelColour color;
		switch (g_server_depth) {
		case 15:
			SPLITCOLOUR15(bgcolour, color)
			;
			break;
		case 16:
			SPLITCOLOUR16(bgcolour, color)
			;
			break;
		case 24:
		case 32:
			SPLITCOLOUR24(bgcolour, color)
			;
			break;
		default:
			color.red = 0;
			color.green = 0;
			color.blue = 0;
			break;
		}
		unsigned int aColor = GG2D_COLOR_A_MASK | ((color.red << 16)
				& GG2D_COLOR_R_MASK) | ((color.green << 8) & GG2D_COLOR_G_MASK)
				| (color.blue & GG2D_COLOR_B_MASK);
		gg2D_beginRender(s_gg2d, 0);
		gg2D_blit(s_gg2d, (gg2DImage*) glyph, aColor, GG2D_BLEND_NORMAL, x, y,
				srcx, srcy, cx, cy);
		gg2D_endRender(s_gg2d);
	}
}
RD_HCURSOR ui_create_cursor(unsigned int x, unsigned int y, int width,
		int height, uint8 * andmask, uint8 * xormask, int bpp) {
	return KD_NULL;
}
void ui_set_cursor(RD_HCURSOR cursor) {
}
void ui_destroy_cursor(RD_HCURSOR cursor) {
}
void ui_set_null_cursor(void) {
}
RD_HCOLOURMAP ui_create_colourmap(COLOURMAP * colors) {
	unsigned int *colorMap = (unsigned int *) kdMalloc(colors->ncolours
			* sizeof(unsigned));
	int i;
	for (i = 0; i < colors->ncolours; i++) {
		COLOURENTRY colorEntry = colors->colours[i];
		colorMap[i] = (colorEntry.red << 16) | (colorEntry.green << 8)
				| colorEntry.blue;
	}
	return colorMap;
}
void ui_destroy_colourmap(RD_HCOLOURMAP map) {
	if (!g_owncolmap)
		kdFree(map);
}
void ui_set_colourmap(RD_HCOLOURMAP map) {
	if (!g_owncolmap) {
		if (g_colmap)
			kdFree(g_colmap);
		g_colmap = (uint32 *) map;
	}
}
void ui_set_clip(int x, int y, int cx, int cy) {
	if (s_gg2d)
		gg2D_setClipRect(s_gg2d, x, y, cx, cy);
}
void ui_reset_clip(void) {
	int width = 0, height = 0;
	if (s_gg2d) {
		gg2D_getScreenSize(s_gg2d, &width, &height);
		ui_set_clip(0, 0, width, height);
	}
}
void ui_bell(void) {
	return;
}
void ui_destblt(uint8 opcode, int x, int y, int cx, int cy) {
	unsigned int aColor = GG2D_COLOR_A_MASK;
	switch (opcode) {
	case 0:
		gg2D_beginRender(s_gg2d, 0);
		aColor = GG2D_COLOR_A_MASK;
		gg2D_drawRect(s_gg2d, aColor, GG2D_BLEND_NORMAL, x, y, cx, cy, 0);
		gg2D_endRender(s_gg2d);
		break;
	case 5:
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
		glFlush();
		break;
	case 15:
		gg2D_beginRender(s_gg2d, 0);
		aColor = GG2D_COLOR_A_MASK | GG2D_COLOR_R_MASK | GG2D_COLOR_G_MASK
				| GG2D_COLOR_B_MASK;
		gg2D_drawRect(s_gg2d, aColor, GG2D_BLEND_NORMAL, x, y, cx, cy, 0);
		gg2D_endRender(s_gg2d);
		break;
	default:
		break;
	}
}
void ui_patblt(uint8 opcode, int x, int y, int cx, int cy, BRUSH * brush,
		int bgcolour, int fgcolour) {
	switch (brush->style)
	{
		case 0: /* Solid */
			ui_rect(x,y,cx,cy,fgcolour);
			break;
	}
}
void ui_screenblt(uint8 opcode, int x, int y, int cx, int cy, int srcx,
		int srcy) {

	printf("ui_screenblt :%d %d %d %d %d %d-> \n", x, y, cx, cy, srcx, srcy);

	uint8* data = KD_NULL;

	uint8* convertedData = KD_NULL;

	gg2DImage* bitmap = KD_NULL;
	gg2DRawImage rawImage;

	data = (uint8*) kdMalloc(cx * cy * 4 * sizeof(uint8));
	convertedData = (uint8*) kdMalloc(cx * cy * 4 * sizeof(uint8));

	if ((data != KD_NULL) && (convertedData != KD_NULL)) {

		kdMemset(data, 0, sizeof(data));
		kdMemset(convertedData, 0, sizeof(convertedData));

		glReadPixels(srcx,  g_height - (srcy + cy), cx, cy, GL_RGBA, GL_UNSIGNED_BYTE,
				data);
		/*
		 * Convert data to correct format
		 */
		for (int i = 0; i < cy; i++) {
			kdMemcpy(convertedData + i * cx * 4, data + (cy - i - 1) * cx * 4,
					cx * 4);
		}
		gg2DRawImage_initialize(&rawImage);

		rawImage.m_width = cx;
		rawImage.m_height = cy;
		rawImage.m_format = GG2D_R8G8B8A8;
		rawImage.m_pitch = cx * 4;
		rawImage.m_buffer = convertedData;

		bitmap = gg2DImage_create(s_gg2d, rawImage.m_width, rawImage.m_height,
				GG2D_R8G8B8A8, GG2D_PERFORMANCE, rawImage.m_pitch,
				rawImage.m_buffer);

		if (s_gg2d) {
			gg2D_beginRender(s_gg2d, 0);
			gg2D_blit(s_gg2d, (gg2DImage*) bitmap, 0xffffffff,
					GG2D_BLEND_NORMAL, x, y, 0, 0, cx, cy);
			gg2D_endRender(s_gg2d);
		}
		gg2DImage_release(bitmap);

		kdFree(data);
		kdFree(convertedData);
	}
	printf("ui_screenblt <- \n");
}
void ui_memblt(uint8 opcode, int x, int y, int cx, int cy, RD_HBITMAP src,
		int srcx, int srcy) {
	if (s_gg2d) {
		gg2D_beginRender(s_gg2d, 0);
		gg2D_blit(s_gg2d, (gg2DImage*) src, 0xffffffff, GG2D_CLIP_ORIGINAL, x,
				y, srcx, srcy, cx, cy);
		gg2D_endRender(s_gg2d);
	}
}
void ui_triblt(uint8 opcode, int x, int y, int cx, int cy, RD_HBITMAP src,
		int srcx, int srcy, BRUSH * brush, int bgcolour, int fgcolour) {
	switch (opcode)
	{
		case 0x69:	/* PDSxxn */
			ui_memblt(ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
			ui_patblt(ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
			break;

		case 0xb8:	/* PSDPxax */
			ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
			ui_memblt(ROP2_AND, x, y, cx, cy, src, srcx, srcy);
			ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
			break;

		case 0xc0:	/* PSa */
			ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
			ui_patblt(ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
			break;

		default:
			unimpl("triblt 0x%x\n", opcode);
			ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
	}
	return;
}
void ui_line(uint8 opcode, int startx, int starty, int endx, int endy,
		PEN * pen) {
	GLfloat vLine[4] = { startx, starty, endx, endy };
	GLubyte indices[] = { 0, 1 };
	glEnableClientState(GL_VERTEX_ARRAY);
	PixelColour color;
	switch (g_server_depth) {
	case 15:
		SPLITCOLOUR15(pen->colour, color)
		;
		break;
	case 16:
		SPLITCOLOUR16(pen->colour, color)
		;
		break;
	case 24:
	case 32:
		SPLITCOLOUR24(pen->colour, color)
		;
		break;
	default:
		color.red = 0;
		color.green = 0;
		color.blue = 0;
		break;
	}
	glLineWidthx(pen->width);
	glColor4f(color.red, color.green, color.blue, 1.0f);
	glVertexPointer(2, GL_FLOAT, 0, vLine);
	glDrawElements(GL_LINES, 2, GL_UNSIGNED_BYTE, indices);
	glDisableClientState(GL_VERTEX_ARRAY);
}
void ui_rect(int x, int y, int cx, int cy, int colour) {
	gg2D_beginRender(s_gg2d, 0);
	PixelColour color;
	switch (g_server_depth) {
	case 15:
		SPLITCOLOUR15(colour, color)
		;
		break;
	case 16:
		SPLITCOLOUR16(colour, color)
		;
		break;
	case 24:
	case 32:
		SPLITCOLOUR24(colour, color)
		;
		break;
	default:
		color.red = 0;
		color.green = 0;
		color.blue = 0;
		break;
	}
	unsigned int aColor = GG2D_COLOR_A_MASK | ((color.red << 16)
			& GG2D_COLOR_R_MASK) | ((color.green << 8) & GG2D_COLOR_G_MASK)
			| (color.blue & GG2D_COLOR_B_MASK);
	gg2D_drawRect(s_gg2d, aColor, GG2D_BLEND_NORMAL, x, y, cx, cy, 0);
	gg2D_endRender(s_gg2d);
}
void ui_polygon(uint8 opcode, uint8 fillmode, RD_POINT * point, int npoints,
		BRUSH * brush, int bgcolour, int fgcolour) {

	GLfloat* v = (GLfloat*) kdMalloc(npoints * 2 * sizeof(GLfloat));

	kdMemset(v, 0, sizeof(v));

	glEnableClientState(GL_VERTEX_ARRAY);

	PixelColour color;

	convertColor(bgcolour, &color);

	glEnable(GL_BLEND);

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glColor4f(color.red, color.green, color.blue, 1.0f);

	for (int i = 0, j = 0; i < npoints; ++i, j += 2) {

		v[j] = point[i].x;

		v[j + 1] = point[i].y;

	}

	glVertexPointer(2, GL_FLOAT, 0, v);

	glDrawArrays(GL_LINE_LOOP, 0, npoints);

	glDisable(GL_BLEND);

	convertColor(fgcolour, &color);

	glColor4f(color.red, color.green, color.blue, 1.0f);

	glVertexPointer(2, GL_FLOAT, 0, v);

	glDrawArrays(GL_LINE_LOOP, 0, npoints);

	glDisableClientState(GL_VERTEX_ARRAY);

	kdFree(v);
}
void ui_polyline(uint8 opcode, RD_POINT * points, int npoints, PEN * pen) {

	GLfloat* v = (GLfloat*) kdMalloc(npoints * 2 * sizeof(GLfloat));

	kdMemset(v, 0, sizeof(v));

	glEnableClientState(GL_VERTEX_ARRAY);

	PixelColour color;
	switch (g_server_depth) {
	case 15:
		SPLITCOLOUR15(pen->colour, color)
		;
		break;
	case 16:
		SPLITCOLOUR16(pen->colour, color)
		;
		break;
	case 24:
	case 32:
		SPLITCOLOUR24(pen->colour, color)
		;
		break;
	default:
		color.red = 0;
		color.green = 0;
		color.blue = 0;
		break;
	}
	glLineWidthx(pen->width);

	glColor4f(color.red, color.green, color.blue, 1.0f);

	for (int i = 0, j = 0; i < npoints; ++i, j += 2) {

		v[j] = points[i].x;

		v[j + 1] = points[i].y;

	}

	glVertexPointer(2, GL_FLOAT, 0, v);

	glDrawArrays(GL_LINES, 0, npoints);

	glDisableClientState(GL_VERTEX_ARRAY);

	kdFree(v);
}
#define degreesToRadian(x) (M_PI * (x) / 180.0)
#define segments 36

void ui_ellipse(uint8 opcode, uint8 fillmode, int x, int y, int cx, int cy,
		BRUSH * brush, int bgcolour, int fgcolour) {
	glTranslatef(x, y, 0.0);
	glEnableClientState(GL_VERTEX_ARRAY);
	GLfloat vertices[segments * 2];
	int count = 0;
	for (GLfloat i = 0; i < 360.0f; i += (360.0f / segments)) {
		vertices[count++] = (cos(degreesToRadian(i)) * cx);
		vertices[count++] = (sin(degreesToRadian(i)) * cy);
	}
	glVertexPointer(2, GL_FLOAT, 0, vertices);

	glDrawArrays((fillmode) ? GL_TRIANGLE_FAN : GL_LINE_LOOP, 0, segments);

	glDisableClientState(GL_VERTEX_ARRAY);
}
void ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x,
		int y, int clipx, int clipy, int clipcx, int clipcy, int boxx,
		int boxy, int boxcx, int boxcy, BRUSH * brush, int bgcolour,
		int fgcolour, uint8 * text, uint8 length) {
	FONTGLYPH *glyph;
	int i, j, xyoffset, x1, y1;
	DATABLOB *entry;

	PixelColour pBgColour, pFgColour;

	convertColor(bgcolour, &pBgColour);
	convertColor(fgcolour, &pFgColour);

	if (boxx + boxcx > g_width)
		boxcx = g_width - boxx;
	unsigned int aColor = 0;
	aColor = GG2D_COLOR_A_MASK | (GG2D_COLOR_R_MASK & (pBgColour.red << 16))
			| (GG2D_COLOR_G_MASK & (pBgColour.green << 8)) | (GG2D_COLOR_B_MASK
			& pBgColour.blue);
	if (boxcx > 1) {
		gg2D_beginRender(s_gg2d, 0);
		gg2D_drawRect(s_gg2d, aColor, GG2D_BLEND_NORMAL, boxx, boxy, boxcx,
				boxcy, 0);
		gg2D_endRender(s_gg2d);
	} else if (mixmode == MIX_OPAQUE) {
		gg2D_beginRender(s_gg2d, 0);
		gg2D_drawRect(s_gg2d, aColor, GG2D_BLEND_NORMAL, clipx, clipy, clipcx,
				clipcy, 0);
		gg2D_endRender(s_gg2d);
	}
	/* Paint text, character by character */
	for (i = 0; i < length;) {
		switch (text[i]) {
		case 0xff:
			/* At least two bytes needs to follow */
			if (i + 3 > length) {
				warning("Skipping short 0xff command:");
				for (j = 0; j < length; j++)
					fprintf(stderr, "%02x ", text[j]);
				fprintf(stderr, "\n");
				i = length = 0;
				break;
			}
			cache_put_text(text[i + 1], text, text[i + 2]);
			i += 3;
			length -= i;
			/* this will move pointer from start to first character after FF command */
			text = &(text[i]);
			i = 0;
			break;

		case 0xfe:
			/* At least one byte needs to follow */
			if (i + 2 > length) {
				warning("Skipping short 0xfe command:");
				for (j = 0; j < length; j++)
					fprintf(stderr, "%02x ", text[j]);
				fprintf(stderr, "\n");
				i = length = 0;
				break;
			}
			entry = cache_get_text(text[i + 1]);
			if (entry->data != NULL) {
				if ((((uint8 *) (entry->data))[1] == 0) && (!(flags
						& TEXT2_IMPLICIT_X)) && (i + 2 < length)) {
					if (flags & TEXT2_VERTICAL)
						y += text[i + 2];
					else
						x += text[i + 2];
				}
				for (j = 0; j < entry->size; j++)
					DO_GLYPH(mixmode, ((uint8 *) (entry->data)), j);
			}
			if (i + 2 < length)
				i += 3;
			else
				i += 2;
			length -= i;
			/* this will move pointer from start to first character after FE command */
			text = &(text[i]);
			i = 0;
			break;

		default:
			DO_GLYPH(mixmode, text, i)
			;
			i++;
			break;
		}
	}
}
void ui_desktop_save(uint32 offset, int x, int y, int cx, int cy) {
	printf("ui_desktop_save : %d %d %d %d-> \n", x, y, cx, cy);

	uint8* data = KD_NULL;
	data = (uint8*) kdMalloc(cx * cy * 4 * sizeof(uint8));
	uint8 *convertedData = (uint8*) kdMalloc(cx * cy * 4 * sizeof(uint8));
	if (data != KD_NULL) {
		kdMemset(data, 0, sizeof(data));
		glReadPixels(x, g_height - (y + cy), cx, cy, GL_RGBA, GL_UNSIGNED_BYTE,
				data);
		/*
		 * Convert data to correct format
		 */
		for (int i = 0; i < cy; i++) {
			kdMemcpy(convertedData + i * cx * 4, data + (cy - i - 1) * cx * 4,
					cx * 4);
		}
		kdFree(data);
		data = KD_NULL;
		offset *= 4;
		cache_put_desktop(offset, cx, cy, cx * 4, 4, (uint8 *) convertedData);
		kdFree(convertedData);
		convertedData = KD_NULL;
	}
	printf("ui_desktop_save <- \n");
}
void ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy) {

	printf("ui_desktop_restore : %d %d %d %d-> \n", x, y, cx, cy);

	gg2DImage* bitmap = KD_NULL;
	gg2DRawImage rawImage;
	uint8 *data = (uint8*) kdMalloc(cx * cy * 4);
	gg2DRawImage_initialize(&rawImage);
	offset *= 4;
	data = cache_get_desktop(offset, cx, cy, 4);

	if (data == NULL)
		return;

	rawImage.m_width = cx;
	rawImage.m_height = cy;
	rawImage.m_format = GG2D_R8G8B8A8;
	rawImage.m_pitch = cx * 4;
	rawImage.m_buffer = data;
	bitmap = gg2DImage_create(s_gg2d, rawImage.m_width, rawImage.m_height,
			GG2D_R8G8B8A8, GG2D_PERFORMANCE, rawImage.m_pitch,
			rawImage.m_buffer);
	if (s_gg2d) {
		gg2D_beginRender(s_gg2d, 0);
		gg2D_blit(s_gg2d, (gg2DImage*) bitmap, 0xffffffff, GG2D_BLEND_NORMAL,
				x, y, 0, 0, cx, cy);
		gg2D_endRender(s_gg2d);
	}
	gg2DImage_release(bitmap);
}
void ui_begin_update(void) {
	return;
}
void ui_end_update(void) {
	return;
}
/*
 * Clipping
 */
void ui_clip_format_announce(uint8 * data, uint32 length) {
	return;
}
void ui_clip_handle_data(uint8 * data, uint32 length) {
	return;
}
void ui_clip_request_failed(void) {
	return;
}
void ui_clip_request_data(uint32 format) {
	return;
}
void ui_clip_sync(void) {
	cliprdr_send_simple_native_format_announce(CF_UNICODETEXT);
}
void ui_clip_set_mode(const char *optarg) {
	return;
}
uint16 ui_get_numlock_state(unsigned int state) {
	return 0;
}
unsigned int read_keyboard_state() {
	unsigned int state = 0;
	return state;
}
